package com.wobito.system.socket;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.wobito.common.constants.SysConstants;
import com.wobito.common.provider.ApplicationContextProvider;
import com.wobito.common.utils.DateUtils;
import com.wobito.pojo.bean.Cabinet;
import com.wobito.service.ICabinetService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * @Author: lambyang
 * @CreateTime: 2024-09-25
 * @Description:
 * @Version: 1.0
 */
@Slf4j
@ServerEndpoint(value = "/websocket/sendOnline/{cabinetCode}")
@Component
public class WebSocketServer {
    // 存储客户端ID和WebSocket会话的映射
    private static ConcurrentHashMap<String, Session> CLIENTS = new ConcurrentHashMap<>();
    //ALL Cabinets
    private static CopyOnWriteArrayList<String> allCabinets = new CopyOnWriteArrayList<String>();
    //在线柜子
    private static CopyOnWriteArrayList<String> onlineCabinets = new CopyOnWriteArrayList<String>();
    //存储每个设备的在线状态
    private static final Map<String, CompletableFuture<Boolean>> waitngForReply = new ConcurrentHashMap<>();

    private static ICabinetService getCabinetService() {
        return ApplicationContextProvider.getApplicationContext().getBean(ICabinetService.class);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("cabinetCode") String cabinetCode) {
        // 连接建立时调用的方法
        CLIENTS.put(cabinetCode, session);
        log.info("新连接建立: {}", session.getId());
    }

    /**
     * 接收消息
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("cabinetCode") String cabinetCode) {
        // 接收到客户端消息时调用的方法
        WebSocketServer.log.info("收到消息: {}", message);
        if (message.equals("CBOK")) {
            CompletableFuture<Boolean> future = waitngForReply.remove(cabinetCode);
            if (future != null) {
                future.complete(true);
            }
        }
        Cabinet cabinet = getCabinetService().selectCabinetByCode(cabinetCode);
        if (ObjectUtil.isNull(cabinet)) {
            return;
        }
        //第一次接收消息时赋值所有柜子
        if (CollectionUtil.isEmpty(allCabinets)) {
            List<String> allCabinetString = getCabinetService().selectCabinetCodeList();
            CopyOnWriteArrayList<String> allCabinet = new CopyOnWriteArrayList<>(allCabinetString);
            allCabinets.addAll(allCabinet);
        }
        if (cabinet.getIsOnline().equals(SysConstants.STATE_SYSTEM_TY)) {
            getCabinetService().updateById(new Cabinet(cabinet.getId(), cabinet.getSequenceCode(), SysConstants.STATE_SYSTEM_ZC, DateUtils.getNowDate()));
        }
        onlineCabinets.add(cabinetCode);
    }

    /**
     * 发送消息
     *
     * @param sequenceCode
     * @param message
     */
    public void sendMessage(String sequenceCode, String message) {
        Session session = CLIENTS.get(sequenceCode);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            log.info("发送消息给设备{}: {}", sequenceCode, message);
        } else {
            log.warn("无法向ID为的客户端发送消息: {}, 会话未打开或不存在.", sequenceCode);
        }
    }

    /**
     * 检查设备是否在线
     *
     * @param sequenceCode
     * @param message
     */
    public CompletableFuture<Boolean> checkEquipmentOnline(String sequenceCode, String message) throws InterruptedException {
        Session session = CLIENTS.get(sequenceCode);
        if (session != null && session.isOpen()) {
            CompletableFuture<Boolean> future = new CompletableFuture<>();
            // 存储这个Future，以便在收到回复时更新它
            waitngForReply.put(sequenceCode, future);
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            log.info("发送消息给设备{}: {}", sequenceCode, message);
        } else {
            log.warn("无法向ID为的客户端发送消息: {}, 会话未打开或不存在.", sequenceCode);
            return CompletableFuture.completedFuture(false);
        }
        // 返回一个可能永远不会完成的Future（如果设备不回复）
        // 或者你可以返回一个已经完成的Future，表示发送失败
        return waitngForReply.getOrDefault(sequenceCode, CompletableFuture.completedFuture(false));
    }

    @OnClose
    public void onClose(Session session) {
        // 连接关闭时调用的方法
        // 遍历映射，找到并移除对应的会话
        for (String clientId : CLIENTS.keySet()) {
            if (CLIENTS.get(clientId).equals(session)) {
                CLIENTS.remove(clientId);
                log.info("连接关闭: {}", session.getId());
                break;
            }
        }
        allCabinets.clear();
        onlineCabinets.clear();
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        // 错误处理
        throwable.printStackTrace();
    }

    //每12分钟执行一次盘点不在线的设备
    @Scheduled(fixedRate = 12 * 60 * 1000)
    public void inventoryEquipmentTask() {
        List<String> offlineCabinet = allCabinets.stream().filter(f -> !onlineCabinets.contains(f)).collect(Collectors.toList());
        CopyOnWriteArrayList<String> offlineCabinets = new CopyOnWriteArrayList<>(offlineCabinet);
        if (CollectionUtil.isNotEmpty(offlineCabinets)) {
            for (String sequenceCode : offlineCabinets) {
                Cabinet cabinet = getCabinetService().selectCabinetByCode(sequenceCode);
                getCabinetService().updateById(new Cabinet(cabinet.getId(), cabinet.getSequenceCode(), SysConstants.STATE_SYSTEM_TY, DateUtils.getNowDate()));
            }
        }
        log.info("检查设备是否在线盘点");
        onlineCabinets.clear();
    }
}
